using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace WingsOverSpace
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        bool paused = true;
        bool startMenu = true;
        const int waveInterval = 500;
        public long updateTicks = 0;
        public int wave = 0;
        int enemiesEscaped = 0;
        public bool spawned = false;
        KeyboardState lastKeyState;
        SpriteFont PauseFont;
        SpriteFont HelpFont;
        public bool gameOver { get; set; }
        public bool victory { get; set; }
        String helpstring = "How to play:";
        String helpstring2 = "Arrow keys: move\nSpace bar: fire\nP: Pause\nP + R: restart\nEsc: Quit";

        public Game1()
        {
            gameOver = false;
            graphics = new GraphicsDeviceManager(this);
            graphics.IsFullScreen = false;
            graphics.PreferredBackBufferHeight = 768;
            graphics.PreferredBackBufferWidth = 1024;
            Content.RootDirectory = "Content";
        }

        Background backgroundOne;
        Background backgroundTwo;
        Background backgroundThree;

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            int index = 0;

            // TODO: Add your initialization logic here
            Components.Add(new Player(this, new Vector2(500, 750)));

            //Initializes ten reusable PlayerShots
            index = 0;
            while (index < 10)
            {
                Components.Add(new PlayerShot(this, new Vector2(0, 0)));
                index++;
            }

            //Makes twenty reusable enemies
            index = 0;
            while (index < 20)
            {
                Components.Add(new Enemy(this, new Vector2(500, 500)));
                index++;
            }

            //Initializes thirty reusable EnemyShots
            index = 0;
            while (index < 30)
            {
                Components.Add(new EnemyShot(this, new Vector2(0, 0)));
                index++;
            }

            //Initializes thirty reusable Asteroids
            index = 0;
            while (index < 30)
            {
                Components.Add(new Asteroid(this, new Vector2(-200, -200)));
                index++;
            }

            //Initializes ten reusable Gravitational Asteroids
            index = 0;
            while (index < 10)
            {
                Components.Add(new GravitationalAsteroid(this, new Vector2(-453, -234), 8000f));
                index++;
            }

            //Initializes ten reusable MoneyUps
            index = 0;
            while (index < 20)
            {
                Components.Add(new moneyPowerUp(this, new Vector2(-100, -100), new Vector2(0,0)));
                index++;
            }

            //Initializes two reusable Lifeups
            index = 0;
            while (index < 2)
            {
                Components.Add(new lifeUp(this, new Vector2(-100, -100), new Vector2(0,0)));
                index++;
            }
                        
            backgroundOne = new Background(this);
            backgroundOne.scale = 1.5f;
            backgroundTwo = new Background(this);
            backgroundTwo.scale = 1.5f;
            backgroundThree = new Background(this);
            backgroundThree.scale = 1.5f;

            lastKeyState = Keyboard.GetState();

            base.Initialize();
     
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            backgroundOne.LoadContent(this.Content, "background01");
            backgroundOne.backgroundPos = new Vector2(0, 0);

            backgroundTwo.LoadContent(this.Content, "background02");
            backgroundTwo.backgroundPos = new Vector2(0, backgroundOne.backgroundPos.Y + backgroundOne.Size.Height);

            backgroundThree.LoadContent(this.Content, "background03");
            backgroundThree.backgroundPos = new Vector2(0, backgroundTwo.backgroundPos.Y + backgroundTwo.Size.Height);

            PauseFont = Content.Load<SpriteFont>("PauseFont");
            HelpFont = Content.Load<SpriteFont>("HelpFont");
            // TODO: use this.Content to load your game content here
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            if (!paused)
                ++updateTicks;

            KeyboardState k = Keyboard.GetState();
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();
            SpawnGameElements();

            if (startMenu == true)
            {
                if (k.IsKeyDown(Keys.B))
                {
                    startMenu = false;
                    paused = false;
                }
            }

            // TODO: Add your update logic here
            if (k.IsKeyDown(Keys.P) && lastKeyState.IsKeyUp(Keys.P) && startMenu == false && gameOver == false && victory == false) {
                if (paused == false)
                {
                    paused = true;
                                 
                }
                else
                {
                    paused = false;
                    (Components[0] as Player).unpausedAtTime = (float)gameTime.TotalGameTime.TotalSeconds;   
                }
            }

            if (gameOver)
            {
                if (k.IsKeyDown(Keys.R))
                {
                    restartGame();
                    gameOver = false;
                }
            }

            if (paused && !startMenu)
            {
                if (k.IsKeyDown(Keys.R))
                {
                    restartGame();
                }
            }

            if (k.IsKeyDown(Keys.Escape))
                Exit();

            if (!paused)
            {
                if (backgroundOne.backgroundPos.Y > backgroundOne.Size.Height)
                    backgroundOne.backgroundPos.Y = backgroundThree.backgroundPos.Y - backgroundThree.Size.Height;
                if (backgroundTwo.backgroundPos.Y > backgroundTwo.Size.Height)
                    backgroundTwo.backgroundPos.Y = backgroundOne.backgroundPos.Y - backgroundOne.Size.Height;
                if (backgroundThree.backgroundPos.Y > backgroundThree.Size.Height)
                    backgroundThree.backgroundPos.Y = backgroundTwo.backgroundPos.Y - backgroundTwo.Size.Height;

                Vector2 scrollDirection = new Vector2(0, 1);
                Vector2 scrollSpeed = new Vector2(0, 100);

                backgroundOne.backgroundPos += scrollDirection * scrollSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;
                backgroundTwo.backgroundPos += scrollDirection * scrollSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;
                backgroundThree.backgroundPos += scrollDirection * scrollSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds;

                base.Update(gameTime);
            }
            else // Game paused
            { }
            lastKeyState = k;

            foreach (WingsDrawableGameComponent comp in Components)
            {
                var c = comp as Player;
                if (c != null)
                {
                    if (c.Lives <= 0)
                    {
                        paused = true;
                        gameOver = true;
                    }
                }
            }
        }

        void restartGame()
        {
            paused = true;
            startMenu = true;
            gameOver = false;
            victory = false;

            enemiesEscaped = 0;
            wave = 0;
            updateTicks = 0;
            spawned = false;
                                 
            foreach (WingsDrawableGameComponent comp in Components)
            {
                var c = comp as Player;
                if (c == null)
                {
                    comp.Visible = false;
                }

                if (c != null)
                {
                    c.reinit(new Vector2(500, 750));
                }
            }
            SpawnGameElements();
        }

        void Victory()
        {
            foreach (WingsDrawableGameComponent comp in Components)
            {
                if (comp is Enemy && comp.Visible == true)
                    enemiesEscaped++;
            }
            paused = true;
            victory = true;
        }

        void changeDirection()
        {
            foreach (WingsDrawableGameComponent comp in Components)
            {
               // (comp as Enemy).Velocity.X = (comp as Enemy).Velocity.X * -1; //TODO: Currently doesn't work because the set method doesn't work for velocity.
            }
        }

         
        protected void SpawnGameElements()
        {
            if (updateTicks >= waveInterval)
            {
                updateTicks = 0;
                wave += 1;
                spawned = false;
            }

            if (!spawned)
            {
                spawned = true;

                switch (wave)
                {
                    case 0:
                        SpawnEnemy(new Vector2(300, 0), new Vector2(50, 100), EnemyType.Two);
                        SpawnEnemy(new Vector2(400, 0), new Vector2(50, 100), EnemyType.One);
                        SpawnEnemy(new Vector2(500, 0), new Vector2(50, 100), EnemyType.Two);
                        SpawnEnemy(new Vector2(600, 0), new Vector2(50, 100), EnemyType.One);
                        SpawnEnemy(new Vector2(700, 0), new Vector2(50, 100), EnemyType.Two);

                        SpawnAsteroid(new Vector2(200, -100), new Vector2(0, 100));
                        SpawnAsteroid(new Vector2(600, -200), new Vector2(0, 100));
                        SpawnAsteroid(new Vector2(400, -230), new Vector2(0, 100));

                        SpawnAsteroid(new Vector2(200, 500), new Vector2(0, 0));
                        SpawnLifeUp(new Vector2(200, 300), new Vector2(0, 0));

                        break;
                    case 1:
                        ClearOffScreenComponents();

                        SpawnEnemy(new Vector2(500, 0), new Vector2(-100, 150), EnemyType.Two);
                        SpawnEnemy(new Vector2(600, 0), new Vector2(-100, 150), EnemyType.One);
                        SpawnEnemy(new Vector2(700, 0), new Vector2(-100, 150), EnemyType.Three);
                        SpawnEnemy(new Vector2(800, 0), new Vector2(-100, 150), EnemyType.One);
                        SpawnEnemy(new Vector2(900, 0), new Vector2(-100, 150), EnemyType.Two);
                        SpawnEnemy(new Vector2(0, 300), new Vector2(130, 0), EnemyType.Four);

                        SpawnGravitationalAsteroid(new Vector2(650, -50), new Vector2(0, 80));


                        break;
                    case 2:
                        ClearOffScreenComponents();

                        SpawnEnemy(new Vector2(300, 0), new Vector2(100, 200), EnemyType.Two);
                        SpawnEnemy(new Vector2(400, 0), new Vector2(100, 200), EnemyType.One);
                        SpawnEnemy(new Vector2(500, 0), new Vector2(100, 200), EnemyType.Three);
                        SpawnEnemy(new Vector2(600, 0), new Vector2(100, 200), EnemyType.Two);
                        SpawnEnemy(new Vector2(700, 0), new Vector2(100, 200), EnemyType.Three);
                        SpawnEnemy(new Vector2(-80, 500), new Vector2(150, 0), EnemyType.Four);
                        SpawnEnemy(new Vector2(1000, 600), new Vector2(-200, 0), EnemyType.Four);

                        SpawnMoneyUp(new Vector2(100, 200), new Vector2(150, 300));

                        break;
                    case 3:
                        ClearOffScreenComponents();

                        SpawnEnemy(new Vector2(300, 0), new Vector2(50, 150), EnemyType.Two);
                        SpawnEnemy(new Vector2(400, 0), new Vector2(50, 250), EnemyType.One);
                        SpawnEnemy(new Vector2(500, 0), new Vector2(50, 350), EnemyType.Five);
                        SpawnEnemy(new Vector2(600, 0), new Vector2(50, 250), EnemyType.One);
                        SpawnEnemy(new Vector2(700, 0), new Vector2(50, 150), EnemyType.Two);

                        SpawnAsteroid(new Vector2(250, 0), new Vector2(0, 230));
                        SpawnLifeUp(new Vector2(400, -300), new Vector2(0, 400));

                        break;
                    case 4:
                        ClearOffScreenComponents();

                        SpawnAsteroid(new Vector2(240, 0), new Vector2(0, 150));
                        SpawnAsteroid(new Vector2(260, -100), new Vector2(0, 150));
                        SpawnAsteroid(new Vector2(500, -50), new Vector2(0, 200));
                        SpawnAsteroid(new Vector2(700, 0), new Vector2(0, 180));
                        SpawnGravitationalAsteroid(new Vector2(450, -80), new Vector2(0, 100));
                        SpawnGravitationalAsteroid(new Vector2(800, -200), new Vector2(0, 150));
                        SpawnAsteroid(new Vector2(360, -20), new Vector2(0, 210));
                        SpawnAsteroid(new Vector2(450, -130), new Vector2(0, 160));
                        SpawnAsteroid(new Vector2(760, -110), new Vector2(0, 180));
                        SpawnGravitationalAsteroid(new Vector2(320, -50), new Vector2(0, 140));
                        SpawnEnemy(new Vector2(0, 550), new Vector2(100, 0), EnemyType.Four);
                        SpawnEnemy(new Vector2(1200, 450), new Vector2(-250, 0), EnemyType.Four);

                        break;
                    case 5:
                        ClearOffScreenComponents();

                        SpawnEnemy(new Vector2(300, 0), new Vector2(100, 150), EnemyType.Two);
                        SpawnEnemy(new Vector2(400, 0), new Vector2(50, 250), EnemyType.One);
                        SpawnEnemy(new Vector2(500, 0), new Vector2(-100, 175), EnemyType.Two);
                        SpawnEnemy(new Vector2(600, 0), new Vector2(100, 190), EnemyType.Three);
                        SpawnEnemy(new Vector2(700, 0), new Vector2(-100, 150), EnemyType.Two);
                        SpawnEnemy(new Vector2(350, -100), new Vector2(100, 190), EnemyType.Four);
                        SpawnEnemy(new Vector2(0, -150), new Vector2(100, 190), EnemyType.Five);

                        break;
                    case 6:
                        ClearOffScreenComponents();

                        Victory();
                        break;                  
                }
            }

        }


        protected void ClearOffScreenComponents()
        {

            foreach (WingsDrawableGameComponent comp in Components)
            {
                if (comp is Enemy || comp is Asteroid || comp is moneyPowerUp || comp is lifeUp || comp is GravitationalAsteroid)
                {
                    if (comp.Position.X < 0 || comp.Position.X > 1200 || comp.Position.Y < 0 || comp.Position.Y > 1000)
                    {
                        if (comp is Enemy && comp.Visible == true)
                            enemiesEscaped++;
                        comp.Visible = false;
                    }
                }
            }
        }

        protected void SpawnEnemy(Vector2 Position, Vector2 Velocity, EnemyType Type)
        {
            for (int index = 11; index < 31; ++index)
            {
                if (Components[index] is Enemy && (Components[index] as Enemy).Visible == false)
                {
                    (Components[index] as Enemy).Visible = true;
                    (Components[index] as Enemy).Position = Position;
                    (Components[index] as Enemy).Velocity = Velocity;
                    if((Components[index] as Enemy).type != Type)
                        (Components[index] as Enemy).ChangeType(Type);
                    break;
                }
            }
        }

        protected void SpawnLifeUp(Vector2 Position, Vector2 Velocity)
        {
            for (int index = 121; index < 123; ++index)
            {
                if (Components[index] is lifeUp && (Components[index] as lifeUp).Visible == false)
                {
                    (Components[index] as lifeUp).Visible = true;
                    (Components[index] as lifeUp).Position = Position;
                    (Components[index] as lifeUp).Velocity = Velocity;
                    break;

                }
            }
        }

        protected void SpawnMoneyUp(Vector2 Position, Vector2 Velocity)
        {
            for (int index = 101; index < 121; ++index)
            {
                if (Components[index] is moneyPowerUp && (Components[index] as moneyPowerUp).Visible == false)
                {
                    (Components[index] as moneyPowerUp).Visible = true;
                    (Components[index] as moneyPowerUp).Position = Position;
                    (Components[index] as moneyPowerUp).Velocity = Velocity;
                    break;
                }
            }
        }

        protected void SpawnAsteroid(Vector2 Position, Vector2 Velocity)
        {

            for (int index = 61; index < 91; ++index)
            {
                if (Components[index] is Asteroid && (Components[index] as Asteroid).Visible == false)
                {
                    (Components[index] as Asteroid).Visible = true;
                    (Components[index] as Asteroid).Position = Position;
                    (Components[index] as Asteroid).Velocity = Velocity;
                    break;
                }
            }
        }

        protected void SpawnGravitationalAsteroid(Vector2 Position, Vector2 Velocity)
        {

            for (int index = 91; index < 101; ++index)
            {
                if (Components[index] is GravitationalAsteroid && (Components[index] as GravitationalAsteroid).Visible == false)
                {
                    (Components[index] as GravitationalAsteroid).Visible = true;
                    (Components[index] as GravitationalAsteroid).Position = Position;
                    (Components[index] as GravitationalAsteroid).Velocity = Velocity;
                    break;
                }
            }
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            // TODO: Add your drawing code here

            
            spriteBatch.Begin();

            if (startMenu)
            {
                spriteBatch.DrawString(PauseFont, "Welcome to:", new Vector2(325, 150),
                    Color.White, 0, new Vector2(0, 0), 0.3f, SpriteEffects.None, 0);
                spriteBatch.DrawString(PauseFont, "Wings Over Space!", new Vector2(100, 250),
                    Color.White, 0, new Vector2(0, 0), 0.4f, SpriteEffects.None, 0);
                spriteBatch.DrawString(PauseFont, "Press 'B' to begin.", new Vector2(200, 450),
                    Color.White, 0, new Vector2(0, 0), 0.3f, SpriteEffects.None, 0);
               spriteBatch.DrawString(HelpFont, helpstring, new Vector2(200, 550),
                    Color.White, 0, new Vector2(0, 0), 1f, SpriteEffects.None, 0);
                spriteBatch.DrawString(HelpFont, helpstring2, new Vector2(400, 550),
                    Color.White, 0, new Vector2(0, 0), 1f, SpriteEffects.None, 0);
            }
            else
            {
                backgroundOne.Draw(this.spriteBatch);
                backgroundTwo.Draw(this.spriteBatch);
                backgroundThree.Draw(this.spriteBatch);
            }
            spriteBatch.End();
            if (!startMenu)
            {
                base.Draw(gameTime);
            }
            spriteBatch.Begin();
            if (paused && !startMenu && !gameOver && !victory)
            {
                spriteBatch.DrawString(PauseFont, "PAUSED", new Vector2(125, 350),
                        Color.White, 0, new Vector2(0, 0), 1f, SpriteEffects.None, 0);
                spriteBatch.DrawString(PauseFont, "Press 'R' to restart!", new Vector2(125, 450),
                    Color.White, 0, new Vector2(0, 0), 0.3f, SpriteEffects.None, 0);
                spriteBatch.DrawString(HelpFont, helpstring, new Vector2(200, 550),
                    Color.White, 0, new Vector2(0, 0), 1f, SpriteEffects.None, 0);
                spriteBatch.DrawString(HelpFont, helpstring2, new Vector2(400, 550),
                    Color.White, 0, new Vector2(0, 0), 1f, SpriteEffects.None, 0);
            }
            if (paused && gameOver)
            {
                spriteBatch.DrawString(PauseFont, "GAME OVER", new Vector2(125, 350),
                    Color.White, 0, new Vector2(0, 0), 0.7f, SpriteEffects.None, 0);
                spriteBatch.DrawString(PauseFont, "Press 'R' to restart!", new Vector2(125, 450),
                            Color.White, 0, new Vector2(0, 0), 0.3f, SpriteEffects.None, 0);
            }
            if (paused && victory)
            {
                spriteBatch.DrawString(PauseFont, "Victory!", new Vector2(170, 350), Color.White, 0, new Vector2(0, 0), 0.7f, SpriteEffects.None, 0);
                spriteBatch.DrawString(PauseFont, "Enemies Escaped: " + enemiesEscaped + "...", new Vector2(125, 450),
                            Color.White, 0, new Vector2(0, 0), 0.3f, SpriteEffects.None, 0);
                spriteBatch.DrawString(PauseFont, "Press 'R' to restart!", new Vector2(125, 550),
                            Color.White, 0, new Vector2(0, 0), 0.3f, SpriteEffects.None, 0);
            }

            spriteBatch.End();
           
        }
    }

}